#include "Mesh.h"

namespace OCPP
{
	Matrix4List Mesh::defaultMatrix4List = Matrix4List();

	Mesh::Mesh(VertexList& vertices, 
		IndiceList& indices, 
		TextureList& textures,
		Matrix4List& instances)
		:mTextureList(textures),
		mIndiceList(indices),
		mVertexList(vertices),
		mInstances(instances),
		mVBOInc(NULL)
	{
		mVAO.bind();
		mVBO = new VBO(vertices);
		mEBO = new EBO(indices);

		mVAO.linkVBO(mVBO, 0, 3, GL_FLOAT, sizeof(Vertex), 0);
		mVAO.linkVBO(mVBO, 1, 3, GL_FLOAT, sizeof(Vertex), (void*)sizeof(Vector3));
		mVAO.linkVBO(mVBO, 2, 3, GL_FLOAT, sizeof(Vertex), (void*)(2 * sizeof(Vector3)));
		mVAO.linkVBO(mVBO, 3, 2, GL_FLOAT, sizeof(Vertex), (void*)(3 * sizeof(Vector3)));

		if (instances.size() > 0)
		{
			mVBOInc = new VBO(instances);
			mVBOInc->bind();
			mVAO.linkVBO(mVBOInc, 4, 4, GL_FLOAT, sizeof(Matrix4), (void*)0);
			mVAO.linkVBO(mVBOInc, 5, 4, GL_FLOAT, sizeof(Matrix4), (void*)(1 * sizeof(Vector4)));
			mVAO.linkVBO(mVBOInc, 6, 4, GL_FLOAT, sizeof(Matrix4), (void*)(2 * sizeof(Vector4)));
			mVAO.linkVBO(mVBOInc, 7, 4, GL_FLOAT, sizeof(Matrix4), (void*)(3 * sizeof(Vector4)));
			// Makes it so the transform is only switched when drawing the next instance
			glVertexAttribDivisor(4, 1);
			glVertexAttribDivisor(5, 1);
			glVertexAttribDivisor(6, 1);
			glVertexAttribDivisor(7, 1);
		}

		mVAO.unbind();
		mVBO->unbind();

		if (mVBOInc)
		{
			mVBOInc->unbind();
		}

		mEBO->unbind();

		mIndiceCount = (uint32)indices.size();
	}

	Mesh::~Mesh()
	{
		CORE_SAFE_DELETE(mVBO);
		CORE_SAFE_DELETE(mVBOInc);
		CORE_SAFE_DELETE(mEBO);
	}

	void Mesh::draw
	(
		Shader& shader,
		Camera& camera,
		Matrix4 matrix
	)
	{
		mVAO.bind();

		shader.setUniformMatrix4fv("model", matrix * getMatirx());

		// Keep track of how many of each type of textures we have
		unsigned int numDiffuse = 0;
		unsigned int numSpecular = 0;
		unsigned int numNormal = 0;
		unsigned int numDisplacement = 0;
		
		for (unsigned int i = 0; i < mTextureList.size(); i++)
		{
			TexturePtr texture = mTextureList[i];
			String num;
			String type = texture->getTextureType();
			if (type == "diffuse")
			{
				num = std::to_string(numDiffuse++);
			}
			else if (type == "specular")
			{
				num = std::to_string(numSpecular++);
			}
			else if (type == "normal")
			{
				num = std::to_string(numNormal++);
			}
			else if (type == "displacement")
			{
				num = std::to_string(numDisplacement++);
			}
			
			shader.setUniform1i((type + num).c_str(),i);
			texture->bind(i);
		}


		if (mInstances.size() == 0)
		{
			glDrawElements(GL_TRIANGLES, mIndiceCount, GL_UNSIGNED_INT, 0);
		}
		else
		{
			glDrawElementsInstanced(GL_TRIANGLES, mIndiceCount, GL_UNSIGNED_INT, 0, mInstances.size());
		}
	}
}